<?php
/* 
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
*/

/**
 * Description of XML
 *
 * @author chaporgin_a
 */
class VA_Form_Form_Mapper_XML extends VA_Form_Form_Mapper {
    /**
     *
     * @var XMLWriter
     */
    private $writer = null;
    /**
     *
     * @var DOMDocument
     */
    private $reader = null;
    public function  __construct() {
        $this->writer = new XMLWriter();
        $this->reader = new DOMDocument();
    }
    /**
     *
     * @param VA_Form_Form $object
     * @return string
     */
    public function FromObject(VA_Form_Form $object) {
        $this->writer->openMemory();
        $this->writer->startDocument( '1.0' , 'utf-8' );
        $this->writer->startElement('form');
        $this->writer->writeAttribute('timeout', $object->getTimeout());
        $this->writer->writeAttribute('timeout_message', $object->getTimeoutMessage());
        $this->writer->writeAttribute('title', $object->getTitle());
        $this->writer->writeAttribute('posted_form_message', $object->getPostedFormMessage());
        $this->writer->writeAttribute('new_form_message', $object->getNewFormMessage());
        $this->writer->writeAttribute('return_message', $object->getReturnMessage());
        $this->writer->writeAttribute('check_with_link', $object->getRemoteCheckLink());
        $this->writer->writeAttribute('post_limit', $object->getPostLimit());
        $this->writer->writeAttribute('post_limit_message', $object->getPostLimitMessage());
        /* @var $element VA_Form_FormElement */
        foreach($object->getElements() as $element)
            $this->elementToXML($element);
        $this->writer->endElement();
        return $this->writer->outputMemory();
    }
    /**
     *
     * @param string $source
     * @return VA_Form_Form
     */
    public function FromSource($source) {
        $this->reader->loadXML($source);
        $xPath = new DOMXPath($this->reader);
        $nodeList = $xPath->query('//form');
        /* @var $formNode DOMElement */
        $formNode = $nodeList->item(0);

//    $list = $this->reader->getElementsByTagName('form');
        $form = new VA_Form_Form();
        $form->setPostedFormMessage($formNode->getAttribute('posted_form_message'))
                ->setReturnMessage($formNode->getAttribute('return_message'))
                ->setTimeoutMessage($formNode->getAttribute('timeout_message'))
                ->setNewFormMessage($formNode->getAttribute('new_form_message'))
                ->setPostLimit($formNode->getAttribute('post_limit'))
                ->setPostLimitMessage($formNode->getAttribute('post_limit_message'))
                ->setTimeout($formNode->getAttribute('timeout'))
                ->setTitle($formNode->getAttribute('title'));
        $cwl = $formNode->getAttribute('check_with_link');
        if(!empty($cwl)) $form->setRemoteCheckLink($cwl);
        $xPath = new DOMXPath($this->reader);
        $nodeList = $xPath->query('element', $formNode);
        $len = $nodeList->length;
        /* @var $domElement DOMElement */
        for($i=0; $i<$len; ++$i)
            $this->processDomElement($nodeList->item($i), $form);
        return $form;
    }
    /**
     * Упаковывает элемент в XML
     * @param VA_Form_FormElement $element
     */
    private function elementToXML(VA_Form_FormElement $element) {
        $this->writer->startElement('element');
        $this->writer->writeAttribute('name', $element->getName());
        $this->writer->writeAttribute('style', $element->getStyle(TRUE));
        $this->writer->writeAttribute('phpClass', get_class($element));
        foreach($element->getAttributeNames() as $name)
            $this->writer->writeAttribute($name, $element->getHTMLTagAttribute($name, TRUE));
        $this->labelToXML($element);
        if($element instanceof VA_Form_FormElement_HtmlBlock) $this->htmlBlockValueToXML($element);
        else $this->rawValueToXML($element);
        $this->validatorsToXML($element);
        switch (get_class($element)) {
            case 'VA_Form_FormElement_Radio':
            case 'VA_Form_FormElement_Checkbox':
                $this->formElement_WithChoiceToXML($element);
                break;
            case 'VA_Form_FormElement_Select':
                $this->formElement_SelectToXML($element);
                break;
            case 'VA_Form_FormElement_File':
            case 'VA_Form_FormElement_MultiFile':
                $this->formElement_FileToXML($element);
                break;
            case 'VA_Form_FormElement_Captcha':
                if(!$element->getLabel()) $element->setLabel('Введите текст с картинки');
                break;
            case 'VA_Form_FormElement_Date':
                $this->formElement_DateToXML($element);
                break;
        }
        $this->writer->endElement();
    }
    private function htmlBlockValueToXML(VA_Form_FormElement_HtmlBlock $element) {
        $this->writer->writeElement('value', base64_encode($element->getValue()));
        $this->writer->writeComment("I use PHP's base64_encode for storing HTML values :( in russian: смотри base64_encode функцию для хранения HTML тут. Конкретно в этом блоке закодировано следующее: \n".$element->getValue());
    }

    /**
     * Превращает элемент выбора даты в обычный XML
     * @param VA_Form_FormElement_Date $element
     */
    private function formElement_DateToXML(VA_Form_FormElement_Date $element) {
        $this->writer->writeElement('minimum', date('Y/m/d', $element->getMinDate()));
        $this->writer->writeElement('maximum', date('Y/m/d', $element->getMaxDate()));
        $this->writer->writeElement('buttons', $element->getShowButtonPanel());
        $this->writer->writeElement('months', $element->getNumberOfMonths());
    }
    /**
     * Превращает селект в нормальный такой XML
     * @param VA_Form_FormElement_Select $element
     */
    private function formElement_SelectToXML(VA_Form_FormElement_Select $element) {
        $this->writer->writeElement('multiple', $element->getSelectionType()==VA_Form_FormElement_Select::TYPE_MULTIPLE?1:0);
        foreach($element->getOptions() as $value => $title) {
            $this->writer->startElement('option');
            $this->writer->writeAttribute('value', $value);
            $this->writer->writeRaw($title);
            $this->writer->endElement();
        }
    }

    private function formElement_FileToXML(VA_Form_FormElement_File $element) {
        $this->writer->startElement('storage');
        $this->writer->writeAttribute('class', get_class($element->getStorage()));
        foreach($element->getStorage()->getInitOptions() as $name => $value) {
            $this->writer->startElement('option');
            $this->writer->writeAttribute('name', $name);
            $this->writer->writeRaw($value);
            $this->writer->endElement();
        }
        $this->writer->endElement();
    }

    /**
     * Превращает радиокнопку в нормальный такой XML
     * @param VA_Form_FormElement_Radio $element
     */
    private function formElement_WithChoiceToXML(VA_Form_FormElement_WithChoice $element) {
        $this->writer->writeElement('groupLayout', $element->getGroupLayout());
        foreach($element->getChoices() as $value => $title) {
            $this->writer->startElement('option');
            $this->writer->writeAttribute('value', $value);
            $this->writer->writeRaw($title);
//      $this->writer->writeAttribute('title', $title);
            $this->writer->endElement();
        }
        $this->writer->writeElement('labelLayout', $element->getLabelPosition());

    }

    private function labelToXML(VA_Form_FormElement $element) {
        $this->writer->writeElement('label', $element->getLabel());

    }
    /**
     * Преобразует "сырое" значение элемента формы в что-то удобоваримое для формата XML
     * @param VA_Form_FormElement $element
     */
    private function rawValueToXML(VA_Form_FormElement $element) {
        $rawValue = $element->getValue();
        if( !empty($rawValue))
            if(is_array($rawValue)) {
                foreach($rawValue as $value) {
                    $this->writer->writeElement('value', $value);
                }
            } elseif(is_scalar($rawValue)) {
                $this->writer->writeElement('value', $rawValue);
            } else
                throw new Exception('tried to map unknown type of value in '.__CLASS__);

    }
    /**
     *
     */
    private function validatorsToXML(VA_Form_FormElement $element) {

        /* @var $validator VA_Form_Validator */
        foreach($element->getValidators() as $validator) {
            $this->writer->startElement('validator');
            $this->writer->writeAttribute('class', get_class($validator));
            foreach($validator->getInitOptions() as $option => $value) {
                $this->writer->startElement('option');
                $this->writer->writeAttribute('name', $option);
                $this->writer->writeRaw(serialize($value));
                $this->writer->endElement();
            }
            $this->writer->endElement();
        }

    }


    private function processDomElement(DOMElement $domElement, VA_Form_Form $form) {

        $className = $domElement->getAttribute('phpClass');
        /* @var $element VA_Form_FormElement */
        $element = new $className;
        $element->setStyleFromLine($domElement->getAttribute('style'));
        switch($className) {
            case 'VA_Form_FormElement_Textbox':
                $this->textboxFromXML($domElement, $element);
                break;
            case 'VA_Form_FormElement_Submit':
            case 'VA_Form_FormElement_Button':
                $this->buttonFromXML($domElement, $element);
                break;
            case 'VA_Form_FormElement_Textarea':
                $this->textareaFromXML($domElement, $element);
                break;
            case 'VA_Form_FormElement_Checkbox':
            case 'VA_Form_FormElement_Radio':
                $this->choiceFromXML($domElement, $element);
                break;
            case 'VA_Form_FormElement_Select':
                $this->selectFromXML($domElement, $element);
                break;
            case 'VA_Form_FormElement_File':
            case 'VA_Form_FormElement_MultiFile':
                $this->fileFromXML($domElement, $element);
                break;
            case 'VA_Form_FormElement_Date':
                $this->dateFromXML($domElement, $element);
                break;
            case 'VA_Form_FormElement_HtmlBlock':
                $this->htmlBlockFromXML($domElement, $element);
                break;
        }
        if($tmp = $domElement->getAttribute('title')) $element->setHTMLTagAttribute('title', $tmp);
        $xPath = new DOMXPath($this->reader);
        $nodeList = $xPath->query('validator', $domElement);

        /* @var $node DOMElement */
        foreach($nodeList as $node) {
            $className = $node->getAttribute('class');
            /* @var $v VA_Form_Validator */
            $v = new $className;
            $tmp = new DOMXPath($this->reader);
            $options = $tmp->query('option', $node);
            $init = array();
            /* @var $o DOMElement */
            foreach($options as $o) $init[$o->getAttribute('name')] = unserialize($o->textContent);
            $v->init($init);
            $element->addValidator($v);
        }

        $element->setName($domElement->getAttribute('name'))
                ->setHTMLTagAttribute('class', $domElement->getAttribute('css'));
        if($tmp = $domElement->getAttribute('label_width')) $element->setHTMLTagAttribute('label_width', $tmp);
        if($tmp = $domElement->getAttribute('label_height')) $element->setHTMLTagAttribute('label_height', $tmp);

        $nodeList = $xPath->query('label', $domElement);
        if($nodeList->length > 0)
            $element->setLabel($nodeList->item(0)->textContent);

        $form->addElement($element);
    }
    private function htmlBlockFromXML(DOMElement $domElement, VA_Form_FormElement_HtmlBlock $element) {
        $xPath = new DOMXPath($this->reader);
        $nodeList = $xPath->query('value', $domElement);
        $node = $nodeList->item(0);

        $element->setValue(base64_decode($node->textContent));
    }
    private function dateFromXML(DOMElement $domElement, VA_Form_FormElement_Date $element) {
        $xPath = new DOMXPath($this->reader);
        $nodeList = $xPath->query('minimum', $domElement);
        $node = $nodeList->item(0);
        $element->setMinDate(strtotime($node->textContent));
        $nodeList = $xPath->query('maximum', $domElement);
        $node = $nodeList->item(0);
        $element->setMaxDate(strtotime($node->textContent));
        $nodeList = $xPath->query('buttons', $domElement);
        $node = $nodeList->item(0);
        $element->setShowButtonPanel($node->textContent?TRUE:FALSE);
        $nodeList = $xPath->query('months', $domElement);
        $node = $nodeList->item(0);
        $element->setNumberOfMonths($node->textContent);
        $nodeList = $xPath->query('value', $domElement);
        $node = $nodeList->item(0);
        $element->setValue($node->textContent);
    }

    private function fileFromXML(DOMElement $domElement, VA_Form_FormElement_File $element) {
        $xPath = new DOMXPath($this->reader);
        $nodeList = $xPath->query('storage', $domElement);
        $node = $nodeList->item(0);
        $options = array();
        $nodeList = $xPath->query('storage', $node);
        foreach($nodeList as $opt)
            $options[$opt->getAttribute('name')] = $opt->textContent;
        $className = $node->getAttribute('class');
        switch($className) {
            case 'VA_Form_File_Storage_LocalFS':
                $storage = new VA_Form_File_Storage_LocalFS($options);
                $element->setStorage($storage);
                break;
        }

    }

    private function selectFromXML(DOMElement $domElement, VA_Form_FormElement_Select $element) {
        $xPath = new DOMXPath($this->reader);
        $nodeList = $xPath->query('multiple', $domElement);
        foreach($nodeList as $node) $element->setSelectionType($node->textContent?VA_Form_FormElement_Select::TYPE_MULTIPLE:VA_Form_FormElement_Select::TYPE_SINGLE);
        $nodeList = $xPath->query('option', $domElement);
        foreach($nodeList as $node) $element->setOption($node->textContent, $node->getAttribute('value'));
//      if($node->getAttribute('selected')) $element->setValue($node->getAttribute('value'));

        $nodeList = $xPath->query('value', $domElement);
        foreach($nodeList as $node) $element->setValue($node->textContent);
    }

    private function choiceFromXML(DOMElement $domElement, VA_Form_FormElement_WithChoice $element) {
        $xPath = new DOMXPath($this->reader);
        $nodeList = $xPath->query('option', $domElement);
        /* @var $node DOMElement */
        foreach($nodeList as $node) $element->setChoice($node->textContent, $node->getAttribute('value'));
        $nodeList = $xPath->query('value', $domElement);
        foreach($nodeList as $node) {
            $element->setValue($node->textContent);
        };
        $nodeList = $xPath->query('groupLayout', $domElement);
        foreach($nodeList as $node) $element->setGroupLayout($node->textContent);
        $nodeList = $xPath->query('labelLayout', $domElement);
        foreach($nodeList as $node) $element->setLabelPosition($node->textContent);
        if(null !== $element->getStyleElement('height')) {
            list($tmp) = $element->getStyleElement('height');
            $tmp = str_replace('px', '', $tmp);
//      var_dump($tmp);exit;
            $element->setHTMLTagAttribute('label_height', $tmp);
        }
    }

    private function textareaFromXML(DOMElement $domElement, VA_Form_FormElement $element) {
        $xPath = new DOMXPath($this->reader);
        $nodeList = $xPath->query('value', $domElement);
        $value = array();
        foreach($nodeList as $node)
            $value[] = $node->textContent;
        $element->setValue(join(' ',$value));
    }
    private function textboxFromXML(DOMElement $domElement, VA_Form_FormElement $element) {
        $xPath = new DOMXPath($this->reader);
        $nodeList = $xPath->query('value', $domElement);
        $value = array();
        /* @var $node DOMElement */
        foreach($nodeList as $node)
            $value[] = $node->textContent;
        $element->setValue(join(' ',$value));
        $element->setHTMLTagAttribute('size', $domElement->getAttribute('size'));
    }

    private function buttonFromXml(DOMElement $domElement, VA_Form_FormElement $element) {
        $xPath = new DOMXPath($this->reader);
        $nodeList = $xPath->query('label', $domElement);
        if($nodeList->length > 0) {
            $element->setValue($nodeList->item(0)->textContent);
        }
    }
}